package xyz.lne.researchgroupmanage.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import xyz.lne.researchgroupmanage.config.FileProperties;
import xyz.lne.researchgroupmanage.entity.File;
import xyz.lne.researchgroupmanage.entity.Research;
import xyz.lne.researchgroupmanage.exception.BadRequestException;
import xyz.lne.researchgroupmanage.exception.FileException;
import xyz.lne.researchgroupmanage.mapper.FileMapper;
import xyz.lne.researchgroupmanage.mapper.ResearchMapper;
import xyz.lne.researchgroupmanage.mapper.SysUserMapper;
import xyz.lne.researchgroupmanage.service.FileService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import xyz.lne.researchgroupmanage.service.dataTransferObject.FileDTO;
import xyz.lne.researchgroupmanage.util.FileConstant;
import xyz.lne.researchgroupmanage.util.enums.FileKind;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author team7
 * @since 2021-01-19
 */
@Service
@RequiredArgsConstructor
public class  FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {
    private final FileProperties fileProperties;
    private final ResearchMapper researchMapper;
    private final SysUserMapper sysUserMapper;

//    课题云盘
    @Override
    public FileDTO uploadFileShare(MultipartFile uploadFile, Integer parentId, Integer researchId, Integer userId) {
//        if (!FileKind.RESEARCH_FILE.getKind().equals(kind))
//            throw new FileException((ImmutableMap.of("上传文件失败", "非云盘类型 kind=" + kind)));
//        是否根目录
       String directory = parentId==0?getDirectoryPath(FileKind.RESEARCH_FILE.getKind(), researchId):getParentDirectoryPath(parentId);
       return upload(uploadFile, directory, FileKind.RESEARCH_FILE.getKind(), parentId, researchId, userId);
    }

//    文档和任务
    @Override
    public FileDTO uploadFileTaskOrDocument(MultipartFile uploadFile, Integer kind, Integer researchId, Integer userId) {
        if (kind<FileKind.DOCUMENT_FILE.getKind()||kind>FileKind.TASK_FILE.getKind())
            throw new FileException((ImmutableMap.of("上传文件失败", "非文档或任务类型 kind=" + kind)));
        String directory = getDirectoryPath(kind, researchId);
        return upload(uploadFile, directory, kind, FileConstant.NO_PARENT, researchId, userId);
    }

    @Override
    public FileDTO uploadImage(MultipartFile image, Integer kind) {
        if (kind<FileKind.USER_ICON.getKind() || kind>FileKind.RESEARCH_GROUP_ICON.getKind())
            throw new FileException(ImmutableMap.of("上传文件失败", "非图片类型 kind=" + kind));
        String directory = getDirectoryPath(kind);
        return upload(image, directory, kind, FileConstant.NO_PARENT, FileConstant.NO_RESEARCH, FileConstant.NO_USER);
    }

//    @Transactional
    FileDTO upload(MultipartFile uploadFile, String directory, Integer kind,
                   Integer parentId, Integer researchId, Integer userId){
        String fileName = generateFileName(directory, uploadFile.getOriginalFilename());
        String path = directory + fileName;
        saveFile(uploadFile, path);
        File file = new File();
        file.setFileName(fileName);
        file.setPath(path);
        file.setKind(kind);
        file.setParentId(parentId);
        file.setResearchId(researchId);
        file.setUserId(userId);
        file.setCreateDate(new Date());
        if (!save(file))throw new FileException(ImmutableMap.of("存储文件失败", "新增数据库失败"));
        return convertFileDTOFromFile(file);
    }
//    云盘建立文件夹
//    @Transactional
    @Override
    public FileDTO makeDirectory(String directoryName, Integer researchId, Integer parentId, Integer userId) {
        String directory = FileConstant.NO_PARENT.equals(parentId)
                ?getDirectoryPath(FileKind.RESEARCH_FILE.getKind(),researchId):getParentDirectoryPath(parentId);
        String path = directory + directoryName + "/";
        java.io.File directoryFile = new java.io.File(path);
        if (directoryFile.exists())throw new FileException(ImmutableMap.of("新建文件夹失败", "文件夹已存在"));
        if (!directoryFile.mkdirs())throw new FileException(ImmutableMap.of("新建文件夹失败", "新建文件夹出现异常 path=" + path));
        File file = new File();
        file.setFileName(directoryName);
        file.setPath(path);
        file.setKind(FileKind.RESEARCH_FILE.getKind());
        file.setIsDir(true);
        file.setParentId(parentId);
        file.setResearchId(researchId);
        file.setUserId(userId);
        file.setCreateDate(new Date());
        if (!save(file))throw new FileException(ImmutableMap.of("新增文件夹失败", "数据库存储失败！"));
        return convertFileDTOFromFile(file);
    }

    @Override
    public FileDTO renameDirectory(Integer fileId, String directoryName, Integer userId) {
        File oldFile = getById(fileId);
        if (oldFile==null)
            throw new BadRequestException(ImmutableMap.of("文件夹重命名失败", "不存在该文件夹"));
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.allEq(ImmutableMap.of("parent_id", oldFile.getParentId(), "file_name", directoryName));
        fileQueryWrapper.last("LIMIT 1");
        File sameFile = getOne(fileQueryWrapper);
        if (sameFile!=null)
            throw new BadRequestException(ImmutableMap.of("文件夹重命名失败", "存在同名的文件夹"));
        oldFile.setUserId(userId);
        oldFile.setFileName(directoryName);
        if(updateById(oldFile))
            return convertFileDTOFromFile(oldFile);
        throw new BadRequestException(ImmutableMap.of("文件夹重命名失败", "参数有误"));
    }

    @Override
    public Boolean deleteFile(Integer fileId) {
//        关注是否updateSelective
        File file = new File();
        file.setFileId(fileId);
        file.setIsDelete(FileConstant.DELETE);
        return updateById(file);
    }

    @Override
    public FileDTO findFileByFileId(Integer fileId) {
//        未判断Delete
        File file = getById(fileId);
        if (file.getIsDir()||file.getIsDelete())
            throw new FileException(ImmutableMap.of("获取文件有误", "文件已被删除或是文件夹"));
        return convertFileDTOFromFile(file);
    }

//    课题组根目录文件 还要限制kind
    @Override
    public List<FileDTO> findResearchFileList(Integer researchId) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.allEq(ImmutableMap.of("research_id", researchId, "kind", FileKind.RESEARCH_FILE.getKind(),
                "parent_id", FileConstant.ROOT, "is_delete", FileConstant.NO_DELETE));
        List<File> fileList = list(fileQueryWrapper);
        return fileList.stream().map(this::convertFileDTOFromFile).collect(Collectors.toList());
    }

    @Override
    public List<FileDTO> findChildrenFileList(Integer parentId) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.allEq(ImmutableMap.of("parent_id", parentId, "kind", FileKind.RESEARCH_FILE.getKind(), "is_delete", FileConstant.NO_DELETE));
        List<File> fileList = list(fileQueryWrapper);
        return fileList.stream().map(this::convertFileDTOFromFile).collect(Collectors.toList());
    }


    private String generateFileName(String directory, String fileName){
        if (!StrUtil.isNotBlank(fileName))throw new FileException(ImmutableMap.of("文件名有误", "文件名" + fileName));
        String[] names = fileName.split("\\.");
        if (names.length!=2)throw new FileException(ImmutableMap.of("文件名有误", "文件名" + fileName));
        String newFileName = names[0] + "_" + IdUtil.simpleUUID() + "." + names[1];
        java.io.File file = new java.io.File(directory + newFileName);
        if (!file.getParentFile().exists()&&!file.getParentFile().mkdirs())
            throw new FileException(ImmutableMap.of("文件不存在父文件夹", "建立父文件夹失败"));
        return file.exists()?generateFileName(directory, fileName):newFileName;
    }

//    云盘文件目录
    private String getParentDirectoryPath(Integer parentId){
        File parentFile = getById(parentId);
        if (parentFile==null || !parentFile.getIsDir() || parentFile.getIsDelete())
            throw new FileException(ImmutableMap.of("获取文件存放路径出错", "不存在父文件夹"));
        return parentFile.getPath();

    }

//    文档OR任务
    private String getDirectoryPath(Integer kind, Integer researchId){
        StringBuilder directoryPath = new StringBuilder(getDirectoryPath(kind));
        Research research = researchMapper.selectById(researchId);
        if (research==null)throw new FileException(ImmutableMap.of("获取文件存放路径出错", "不存在该课题组"));
        directoryPath.append(research.getAccount()).append("/");
        return directoryPath.toString();
    }

//    图片
    private String getDirectoryPath(Integer kind){
        if (FileKind.USER_ICON.getKind().equals(kind)){
            return fileProperties.getUserIcon();
        }else if(FileKind.RESEARCH_ICON.getKind().equals(kind)){
            return fileProperties.getResearchIcon();
        }else if (FileKind.RESEARCH_GROUP_ICON.getKind().equals(kind)){
            return fileProperties.getResearchGroupIcon();
        }else if (FileKind.DOCUMENT_FILE.getKind().equals(kind)){
            return fileProperties.getDocumentFile();
        }else if (FileKind.TASK_FILE.getKind().equals(kind)){
            return fileProperties.getTaskFile();
        }else if (FileKind.RESEARCH_FILE.getKind().equals(kind)){
            return fileProperties.getResearchFile();
        }
        throw new FileException(ImmutableMap.of("获取文件存放路径出错", "kind=" + kind));
    }

//    存储文件
    private void saveFile(MultipartFile file, String filePath) {
        try {
            if (file.isEmpty()) throw new FileException(ImmutableMap.of("文件写入失败", "空文件"));
            byte[] bytes = file.getBytes();
            Path path = Paths.get(filePath);
            Files.write(path, bytes);
        }catch (IOException ioException){
            throw new FileException(ImmutableMap.of("文件写入", "文件写入出现异常，请重试"));
        }
    }

    private FileDTO convertFileDTOFromFile(File file){
        FileDTO fileDTO = new FileDTO();
        BeanUtils.copyProperties(file, fileDTO);
        fileDTO.setUserName(sysUserMapper.selectById(file.getUserId()).getName());
        return fileDTO;
    }

}
